package morenoapp.components.thinkgraph;

import edu.umd.cs.piccolo.PCanvas;
import edu.umd.cs.piccolo.PNode;
import edu.umd.cs.piccolo.event.PDragSequenceEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.nodes.PText;
import edu.umd.cs.piccolo.util.PAffineTransform;
import edu.umd.cs.piccolo.util.PPaintContext;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
import morenoapp.MorenoApp;
import morenoapp.components.thinkgraph.model.DisplayFilter;
import morenoapp.components.thinkgraph.model.DisplayFilterListener;
import morenoapp.components.thinkgraph.node.BullEyeTargetNode;
import morenoapp.components.thinkgraph.node.RespondentNode;
import morenoapp.components.thinkgraph.node.VectorNode;
import morenoapp.components.thinkgraph.node.WithTooltip;
import morenoapp.components.thinkgraphv2.nodes.CorrectionNode;
import morenoapp.components.thinkgraphv2.nodes.RelationshipItemNode;
import morenoapp.components.thinkgraphv2.nodes.RespondentItemNode;
import morenoapp.components.thinkgraphv2.nodes.SociogramNode;
import morenoapp.model.app.MorenoApplicationListener;
import morenoapp.model.app.MorenoApplicationVetoException;
import morenoapp.model.data.MorenoRespondentFace;
import morenoapp.model.listeners.MorenoModelAdapter;

/**
 *
 * @author dmitry.mamonov
 */
public class PiccoloGraph extends PCanvas implements MorenoApplicationListener, DisplayFilterListener {
    DisplayFilter filter;
    PAffineTransform cameraNullTransform;
    PText tooltipNode = new PText();
    SociogramNode sociogramNode;
    PNode contrainerNode = new PNode();
    PNode graph = null;
    List<PNode> modelNodes;
    List<RespondentItemNode> respondentsNodeList;
    List<CorrectionNode> correctionsNodeList;
    List<RelationshipItemNode> relationshipNodeList;

    public PiccoloGraph(DisplayFilter _filter) {
        super();

        this.filter = _filter;

        this.removeInputEventListener(this.getPanEventHandler());
        this.filter.addDisplayFilterLisneter(new DisplayFilterListener() {
            public void onDisplayFilterChanged() {
                System.out.println("Display filter changed: "); //NOI18N
                if (filter.isAccurateDraw()) {
                    setDefaultRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
                    //setAnimatingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
                    //setInteractingRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING);
                } else {
                    setDefaultRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
                    //setAnimatingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
                    //setInteractingRenderQuality(PPaintContext.LOW_QUALITY_RENDERING);
                }
            }
        });


        cameraNullTransform = getCamera().getViewTransform();
        graph.addChild(new BullEyeTargetNode(filter));
        getLayer().addChild(graph);

        MorenoApp.getApplicationData().addApplicationListener(this);
        MorenoApp.getApplicationData().getMorenoModelProxy().addModelListener(new MorenoModelAdapter() {
            @Override
            public void onQuestionsListChanged() {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        loadGraphDataFromModel();
                    }
                });
            }
        });

        //Configure Tooltip
        condifureTooltipHandler();
    }

    /**
     * При изменении размеров видимой области
     * устанавливает положение центра координат в её центре.
     *
     * @param x
     * @param y
     * @param w
     * @param h
     */
    @Override
    public void setBounds(int x, int y, int w, int h) {
        System.out.println("bounds: " + x + ", " + y + ", " + w + ", " + h); //NOI18N
        getCamera().setViewTransform(cameraNullTransform);
        getCamera().translateView(w / 2, h / 2);
        super.setBounds(x, y, w, h);
    }

    private void condifureTooltipHandler() {
        tooltipNode.setPickable(false);
        getCamera().addChild(tooltipNode);
    }

    private void updateToolTip(PInputEvent _event) {
        String tooltipString = null;
        if (_event != null && filter.isDisplayTooltips()) {
            PNode node = _event.getInputManager().getMouseOver().getPickedNode();
            while (node != null) {
                //System.out.println("trace for tooltip: " + node);
                if (node instanceof WithTooltip) {
                    tooltipString = ((WithTooltip) node).getTooltipText();
                    break;
                }
                node = node.getParent();
            }
            Point2D p = _event.getCanvasPosition();
            _event.getPath().canvasToLocal(p, getCamera());
            tooltipNode.setOffset(p.getX() + 12, p.getY() - 23);
        }

        tooltipNode.setText(tooltipString);
    }

    private void clearModelData() {
        graph.removeChildren(modelNodes);
        modelNodes.removeAll(modelNodes);
    }

    public void loadGraphDataFromModel() {
        clearModelData();
        graph.addInputEventListener(new NodeDragHandler());
        Map<MorenoRespondentFace, RespondentNode> nodesMap = new HashMap<MorenoRespondentFace, RespondentNode>();
        for (MorenoRespondentFace resp : MorenoApp.getApplicationData().getMorenoModelProxy().getRespondentsList()) {
            RespondentNode rNode = new RespondentNode(resp, filter);
            nodesMap.put(resp, rNode);
            //rNode.addInputEventListener(new NodeDragHandler());

            graph.addChild(rNode);
            modelNodes.add(rNode);

            System.out.println("node: " + rNode + " pos " + rNode.getGlobalTranslation()); //NOI18N
        }

        for (RespondentNode rNode : nodesMap.values()) {
            rNode.configureRelationships(nodesMap);
        }

        invalidate();
        validate();
    }

    public void saveGraphDataIntoModel() {
        for (PNode node : modelNodes) {
            if (node instanceof RespondentNode) {
                MorenoRespondentFace respondent = ((RespondentNode) node).getRespondentFace();
                //respondent.setGraphPosition(new Point2D.Double(node.getX(), node.getY()));
            }
        }
    }

    public void onSaveDoCommitChanges() throws MorenoApplicationVetoException {
        saveGraphDataIntoModel();
    }

    public void afterModelSaved(File target) {
        //pass
    }

    public void afterModelOpened(File source) {
        //pass
    }

    public void onDisplayFilterChanged() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    /// <summary>
    /// Simple event handler which applies the following actions to every node it is called on:
    ///   * Turn node red when the mouse goes over the node
    ///   * Turn node white when the mouse exits the node
    ///   * Drag the node, and associated edges on mousedrag
    /// It assumes that the node's Tag references an ArrayList with a list of associated
    /// edges where each edge is a PPath which each have a Tag that references an ArrayList
    /// with a list of associated nodes.
    /// </summary>
    class NodeDragHandler extends PDragSequenceEventHandler {
        public NodeDragHandler() {
            getEventFilter().setMarksAcceptedEventsAsHandled(true);
        }

        @Override
        public void mouseEntered(PInputEvent _event) {
            PNode node = _event.getPickedNode();
            if (!_event.isLeftMouseButton()) {
                if (checkNodeType(node, RespondentNode.class)) {
                    node.setPaint(new Color(0xffff88));
                } else if (checkNodeType(node, VectorNode.class)) {
                    setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
                }
            }
            updateToolTip(_event);
        }

        @Override
        public void mouseExited(PInputEvent _event) {
            PNode node = _event.getPickedNode();
            if (!_event.isLeftMouseButton()) {
                if (checkNodeType(node, RespondentNode.class)) {
                    node.setPaint(null);
                } else if (checkNodeType(node, VectorNode.class)) {
                    setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
                }
            }
            updateToolTip(null);
        }

        @Override
        public void drag(PInputEvent _event) {
            PNode node = _event.getPickedNode();
            if (checkNodeType(node, RespondentNode.class)) {
                node.setX(node.getX() + _event.getDelta().width);
                node.setY(node.getY() + _event.getDelta().height);
            }
            updateToolTip(_event);
        }

        private boolean checkNodeType(PNode _node, Class<?> _type) {
            PNode current = _node;
            while (current != null) {
                if (_type.isAssignableFrom(current.getClass())) {
                    return true;
                }
                current = current.getParent();
            }
            return false;
        }
    }

    public void saveAsImage(File targetFile) throws IOException {
        Dimension componentSize = this.getSize();
        BufferedImage img = new BufferedImage(componentSize.width,
                componentSize.height,
                BufferedImage.TYPE_INT_RGB);
        Graphics2D graphics = img.createGraphics();
        graphics.fillRect(0, 0, img.getWidth(), img.getHeight());
        this.paint(graphics);
        ImageIO.write(img, "png", targetFile); //NOI18N
    }
}



